import { fetchHtml } from '@/utils/utils';
import { Injectable } from '@nestjs/common';
const cheerio = require('cheerio');
import { source } from '@/config/reader.source';
import { BusinessException } from '@/common/exceptions/business.filter';
@Injectable()
export class ReaderService {
  /**
   * @param url
   * @param content
   * @returns 查询
   */
  async searchBook(params) {
    const res = await this._getDeepBook(params);
    if (!res) {
      return false;
    }
    const result = await this._handlerSearch(res);
    return result;
  }
  async _getDeepBook(params) {
    let bookList = [];
    const { pageSize, pageNo, origins } = params;
    let allowSource = source;
    if (origins.length) {
      allowSource = source.filter((i) => origins.includes(i.origin));
    }

    const start = (pageNo - 1) * pageSize;
    const end = pageSize * pageNo;
    const requestList = allowSource.slice(start, end);

    for (let index = 0; index < requestList.length; index++) {
      try {
        const element = requestList[index];
        let res;
        if (element.search.method == 'get') {
          const url = this._formatUrl(
            element.origin,
            element.search.pathname,
            params,
            element.search.query,
          );
          res = await fetchHtml(url);
        } else if (element.search.method == 'post') {
          const [url, query] = this._formatPostUrl(
            element.origin,
            element.search.pathname,
            params,
            element.search.query,
          );
          res = await fetchHtml(url, element.search.method, query, {});
        }
        if (res) {
          let value = this._parseSearchContent(res, element) as any;
          if (params.limit) {
            value = value.slice(0, params.limit);
          }
          bookList = bookList.concat(value);
        }
      } catch (error) {}
    }

    return bookList;
  }
  _parseSearchContent(res: any, item: any) {
    const bookList = [] as any;
    const { rules } = item.search;
    const $ = cheerio.load(res);
    // 第一步：获取类
    let wrapperContainer = $(rules.wrapContainer.selector);
    // 第二步：遍历每个元素，提取其内部具体内容
    wrapperContainer.each((index, element) => {
      // 封面 书名 作者 目录地址 分类 最新章节 最近更新时间
      const { image, bookName, author, pathname, categories, latestChapter } =
        rules.infomation;

      const bookInfo = {
        origin: item.origin,
        bookName: '',
        author: '',
        image: '',
        pathname: '',
        categories: '',
        latestChapter: '',
      };

      const handlers = [
        ['image', image],
        ['bookName', bookName],
        ['author', author],
        ['pathname', pathname],
        ['categories', categories],
        ['latestChapter', latestChapter],
      ];

      handlers.forEach((arr) => {
        const [key, rule] = arr;

        if (!rule) return;

        const ele = $(element).find(rule.selector);

        if (ele) {
          if (key === 'image') {
            bookInfo[key] = ele.attr(rule.attr || 'src') || '';
          } else if (key === 'pathname') {
            bookInfo[key] = ele.attr(rule.attr || 'href') || '';
          } else {
            bookInfo[key] = ele.text() || '';
          }

          if (rule.subPath) {
            bookInfo[key] = `${item.origin}${bookInfo[key]}`;
          }
        }

        if (rule?.hasOwnProperty('nthchild')) {
          const num = rule.nthchild;
          if (ele?.length >= num + 1) {
            const text = ele.eq(num).text() || '';
            bookInfo[key] = text;
          }
        }
        if (rule?.handler) {
          Object.entries(rule.handler).forEach(
            ([type, value]: [string, string]) => {
              switch (type) {
                case 'replace':
                  bookInfo[key] = bookInfo[key].replace(
                    new RegExp(value, 'g'),
                    rule.handler?.replaceValue || '',
                  );
                  break;
                default:
                  break;
              }
            },
          );
        }

        bookInfo[key] = bookInfo[key].trim();
      });
      bookList.push(bookInfo);
    });

    return bookList;
  }
  _handlerSearch(bookList: any) {
    bookList = bookList.filter((i) => i.bookName && i.pathname);
    return bookList;
  }
  _formatUrl(origin, path, params, query) {
    origin = origin.endsWith('/') ? origin.slice(0, origin.length - 1) : origin;
    path = path.startsWith('/') ? path : `/${path}`;
    let str = `${origin}${path}?`;
    let searchParams = ``;

    Object.entries(query).forEach(([key, value]: [string, any]) => {
      if (typeof value === 'string') {
        searchParams += `&${key}=${encodeURIComponent(value)}`;
      }
      if (value instanceof Object) {
        switch (value.type) {
          case 'date':
            searchParams += `&${key}=${new Date().getTime()}`;
            break;
          case 'query':
            searchParams += `&${key}=${encodeURIComponent(params.fuzzy)}`;
            break;
          default:
            break;
        }
      }
    });

    str += searchParams.slice(1, searchParams.length);
    return str;
  }
  _formatPostUrl(origin, path, params, query) {
    origin = origin.endsWith('/') ? origin.slice(0, origin.length - 1) : origin;
    path = path.startsWith('/') ? path : `/${path}`;
    let str = `${origin}${path}`;
    let searchParams = {};

    Object.entries(query).forEach(([key, value]: [string, any]) => {
      if (typeof value === 'string') {
        searchParams[key] = value;
      }
      if (value instanceof Object) {
        switch (value.type) {
          case 'date':
            searchParams[key] = new Date().getTime();
            break;
          case 'query':
            searchParams[key] = params.fuzzy;
            break;
          default:
            break;
        }
      }
    });
    const res = [str, searchParams];

    return res;
  }
  /**
   *
   * @param url
   * @param content
   * @returns 获取内容
   */
  async getPageContent(params) {
    const content = await this._getDeepPage(params?.url || '');
    if (!content) {
      return false;
    }
    const result = await this._handlerPage(content, params?.url || '');
    return result;
  }

  async _getDeepPage(url: string, content: any = {}) {
    try {
      const res = await fetchHtml(url);
      if (res) {
        const parse = new URL(url);
        const targetSource = source.find((i) => i.origin === parse.origin);
        if (targetSource) {
          const $ = cheerio.load(res);
          const { wrapContainer, titleContainer, pagination, lineBreak } =
            targetSource.content.rules;

          if (!content.isParse) {
            content = {
              isParse: true,
              text: '',
              title: $(titleContainer.selector).text() || '',
            };
          }
          if (lineBreak === 'br') {
            const txtElement = $(wrapContainer.selector);
            // 将 <br> 标签替换为 \n
            const modifiedHtml = txtElement.html().replace(/<br\s*\/?>/g, '\n');
            // 获取修改后的文本内容
            const text = cheerio.load(modifiedHtml).text();
            content.text += text;
          } else {
            $(wrapContainer.selector).each((index, element) => {
              const text = $(element).text();
              content.text += `${text}\n`;
            });
          }

          if (pagination) {
            const nextPagination = $(pagination.selector).last();
            const nextPath = nextPagination.attr('href');
            const nextPathText = nextPagination.text().trim();

            if (nextPath.startsWith('/') && nextPathText === '下一页') {
              content = await this._getDeepPage(
                `${parse.origin}${nextPath}`,
                content,
              );
            }
          }
        }
      }
      return false;
    } catch (error) {
      return false;
    }
    return content;
  }
  async _handlerPage(content: any, url: string) {
    const parse = new URL(url);
    const targetSource = source.find((i) => i.origin === parse.origin) as any;
    if (targetSource) {
      let handler = targetSource?.content?.handler;

      let { text, title } = content;
      text = text.replace(/\n\s*\n/g, '\n');
      const data = text
        .split(/\n/)
        .map((i) => i.trim())
        .filter((i) => i);
      handler?.replace?.forEach((r) => {
        if (r.type === 'Array') {
          data.splice(r.value, 1);
        }
      });
      return {
        data,
        text,
        title,
      };
    }
    return {};
  }

  /**
   * @param url
   * @param content
   * @returns 获取目录
   */
  async getCatalogs(params) {
    let content = await this._getDeepHtml(
      params?.url || 'https://www.deqixs.com',
    );
    if (!content) {
      return false;
    }
    const result = this._handlerCatalogs(content);
    return result;
  }
  async _getDeepHtml(url: string, content: any = {}) {
    try {
      let res = await fetchHtml(url);
      if (res) {
        const parse = new URL(url);
        const targetSource = source.find((i) => i.origin == parse.origin);
        if (targetSource) {
          const $ = cheerio.load(res);

          const { wrapContainer, infomation, pagination, redirect } =
            targetSource.catalogs.rules;

          let info = {
            bookName: '',
            author: '',
            image: '',
            categories: '',
            latestChapter: '',
            latestUpdateTime: '',
            description: '',
          };
          const reflect = [
            ['bookName', infomation.bookName],
            ['author', infomation.author],
            ['image', infomation.image],
            ['categories', infomation.categories],
            ['latestChapter', infomation.latestChapter],
            ['latestUpdateTime', infomation.latestUpdateTime],
            ['description', infomation.description],
          ];

          reflect.forEach(([key, rule]: [string, any]) => {
            let node = $(rule.selector);
            if (rule?.hasOwnProperty('nthchild')) {
              const num = rule.nthchild;
              if (node?.length >= num + 1) {
                node = node.eq(num);
              }
            }
            if (rule.type === 'element') {
              if (rule.attr) {
                info[key] = node.attr(rule.attr) || '';
              } else {
                info[key] = node.text() || '';
              }

              if (rule.subPath) {
                info[key] = `${parse.origin}${info[key]}`;
              }
            } else {
              info[key] = node.attr('content');
            }

            // 处理文字信息
            if (rule?.handler) {
              Object.entries(rule.handler).forEach(
                ([type, value]: [string, string]) => {
                  switch (type) {
                    case 'replace':
                      if (value) {
                        info[key] = info[key].replace(
                          new RegExp(value, 'g'),
                          rule.handler?.replaceValue || '',
                        );
                      }

                      break;
                    default:
                      break;
                  }
                },
              );
            }
          });

          if (!content.isParse) {
            content = {
              isParse: true,
              origin: parse.origin,
              pathname: parse.pathname,
              links: [],
              ...info,
            };
          }
          const wrapperContainer = $(wrapContainer.selector);
          wrapperContainer.each((index, element) => {
            let href = $(element).attr('href');
            if (redirect) {
              href = `${parse.pathname}${href}`;
            }
            content.links.push({
              href,
              text: $(element).text(),
            });
          });
          if (redirect) {
            let node = $(redirect.selector);
            if (redirect?.hasOwnProperty('nthchild')) {
              const num = redirect.nthchild;
              if (node?.length >= num + 1) {
                node = node.eq(num);
              }
            }
            const redirectUrl = node.attr('href');
            if (redirectUrl) {
              const nextUrl = `${parse.origin}${redirectUrl}`;

              content = await this._getDeepHtml(nextUrl, content);
            }
          }
          if (pagination) {
            const nextPagination = $(pagination.selector).last();

            let nextPath = nextPagination.attr('href');
            let nextButtonName = nextPagination.text();

            if (nextPath && nextButtonName === '下一页') {
              const nextUrl = pagination.fullpath
                ? `${parse.origin}${parse.pathname}${nextPath}`
                : `${parse.origin}${nextPath}`;

              content = await this._getDeepHtml(nextUrl, content);
            }
          }
        } else {
          return false;
        }
      } else {
        return false;
      }
    } catch (error) {
      return false;
    }
    return content;
  }
  async _handlerCatalogs(content) {
    const array = content.links || [];
    // 过滤重复的 href
    const uniqueArray = Array.from(new Set(array.map((item) => item.href))).map(
      (href) => array.find((item) => item.href === href),
    );

    // 按照 xxxx.html 从小到大排序
    uniqueArray.sort((a, b) => {
      const numA = parseInt(a.href.split('/').pop().split('.html')[0]);
      const numB = parseInt(b.href.split('/').pop().split('.html')[0]);
      return numA - numB;
    });

    content.links = uniqueArray;
    content.origins = [
      {
        origin: content.origin,
        pathname: content.pathname,
      },
    ];
    return content;
  }
}
